//this is the worker thread,doing
// :connect to a named pipe,issue command and wait for msg
//: on msg recived,use NPN_PluginThreadAsyncCall to make browser callback a function to change the html

#include <windows.h>
#include <vector>
#include <string>
#include "npapi.h"
#include "worker.h"

extern  void  logmsg(const char *msg);

static char read_buf[512];

extern void schedule_showprog(int prog);

VOID WINAPI onWrited(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransfered,
					 LPOVERLAPPED lpOverlapped);

VOID WINAPI onReaded(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransfered,
					 LPOVERLAPPED lpOverlapped)
{
	Worker *worker = (Worker*)lpOverlapped;
	
	char errbuf[256];
	if(dwErrorCode != 0){//109 pipe broken
		sprintf(errbuf, "pipe read error %d, %x\n" , dwErrorCode,GetLastError());
		logmsg(errbuf);
		worker->mIOerror = dwErrorCode;
		::SetEvent(worker->hEvent);
		return;
	}else{
		
		read_buf[dwNumberOfBytesTransfered] = '\0';
		sprintf(errbuf,"read %d bytes : %s\n",dwNumberOfBytesTransfered,read_buf);
		logmsg(errbuf);
		return;
	}

}



VOID WINAPI onWrited(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransfered,
					 LPOVERLAPPED lpOverlapped)
{
	Worker *worker = (Worker*)lpOverlapped;
	
	char errbuf[256];
	if(dwErrorCode != 0){
		worker->mIOerror = dwErrorCode;
		sprintf(errbuf, "pipe write error %d,%x\n" ,dwErrorCode, GetLastError());
		logmsg(errbuf);
		::SetEvent(worker->hEvent);
		return;
	}else
	{
		logmsg("write ok\n");
	}
	//worker->wait_response();
}


void Worker::wait_response()
{
	::ReadFileEx(hPipe,read_buf,512,this,onReaded);
}


/*DWORD WINAPI WorkerThread(void *data)
{
	char errbuf[256];
	char buf[4096];

	Worker * worker = (Worker*)data;
	
	
	//sprintf(buf,"CRECO %d %d %d %d",worker->left,worker->top,worker->width,worker->height);
	//::WriteFileEx(worker->hPipe,buf,strlen(buf),worker,onWrited);
	worker->run= true;
	while(worker->run){

		DWORD ret = ::WaitForSingleObjectEx(worker->hEvent,500,true);
		switch(ret){
			case WAIT_OBJECT_0:
				{
					worker->run = false;
				}
				break;
			case WAIT_IO_COMPLETION:
				break;
			case WAIT_TIMEOUT:
				break;
			case WAIT_FAILED:
				sprintf(errbuf, "wait error %x\n" , GetLastError());
				logmsg(errbuf);
				break;
		}

	}

	worker->run = false;
	::CloseHandle(worker->hPipe);
	worker->hPipe = 0;
	return 0;
}*/

void Worker::request_async(std::string cmd)
{
	::WriteFileEx(hPipe,cmd.c_str(),cmd.length(),this,onWrited);
}

bool Worker::request_sync(std::string cmd,std::string & response)
{
	char err[100];
	bool result = false;
	
	DWORD res  = 0;
	if(!::WriteFileEx(hPipe,cmd.c_str(),cmd.length(),this,onWrited)){
		logmsg("write error,retry init pipe\n");
		close();
		init();
		return false;
	}
	while(true)
	{
		logmsg("wait write finish\n");
		res = ::WaitForSingleObjectEx(hEvent,300,true);
		if(res == WAIT_OBJECT_0){//
			logmsg("error pipe\n");
			 break;
		}else if(res == WAIT_IO_COMPLETION){
			 //result = true;
			 //response = read_buf;
			 break;
		}else if(res == WAIT_TIMEOUT){
			continue;
		}else if(res == WAIT_FAILED){
			logmsg("WAIT_FAILED\n");
			break;
		}
	}

	if(res == WAIT_IO_COMPLETION){
		::ReadFileEx(hPipe,read_buf,512,this,onReaded);
		while(true)
		{
			logmsg("wait read finish\n");
			DWORD res = ::WaitForSingleObjectEx(hEvent,300,true);
			if(res == WAIT_OBJECT_0){//
				logmsg("error pipe\n");
				 break;
			}else if(res == WAIT_IO_COMPLETION){
				 result = true;
				 response = read_buf;
				 break;
			}else if(res == WAIT_TIMEOUT){
				continue;
			}else if(res == WAIT_FAILED){
				logmsg("WAIT_FAILED\n");
				break;
			}
		};
	}
	
	return result;
}

bool Worker::init()
{
	DWORD thread_id;
	mIOerror = 0;
	ResetEvent(this->hEvent);
	
	hPipe = ::CreateFileA("\\\\.\\pipe\\dmm_control",					//name
		GENERIC_READ|GENERIC_WRITE,		//acess type
		0,								//shared mode
		NULL,							//security attibute.
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED,NULL);

	if(hPipe == INVALID_HANDLE_VALUE){
		char errbuf[512];
		sprintf(errbuf,"can not open the pipe %x\n",GetLastError());
		logmsg(errbuf);
		
		return false;
		
	}


	DWORD dwMode = PIPE_READMODE_MESSAGE; 
	BOOL fSuccess = SetNamedPipeHandleState( 
		hPipe,    // pipe handle 
		&dwMode,  // new pipe mode 
		NULL,     // don't set maximum bytes 
		NULL);    // don't set maximum time 
	if ( ! fSuccess) 
	{
		char errbuf[512];
		sprintf(errbuf,"SetNamedPipeHandleState failed. GLE=%d\n", GetLastError() ); 
		logmsg(errbuf);
		return false;
	}

	mbConnected = true;
	return true;

	/*hThread = ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WorkerThread,this,0,&thread_id);

	if(hThread) 
		return true;
	else
		return false;*/
}


bool Worker::close(){

	BOOL res = ::CancelIoEx(hPipe,this);
	SetEvent(this->hEvent);
	/*if(::WaitForSingleObject(hThread,1000) != WAIT_OBJECT_0)
    ::TerminateThread(hThread,-1);*/
	return true;
}


Worker::Worker()
{
	mbConnected = false;

	ZeroMemory(this,sizeof(OVERLAPPED));
	this->hEvent = ::CreateEvent(NULL,TRUE,FALSE,NULL);
}


Worker::~Worker()
{
	::CloseHandle(this->hEvent);
	hEvent = 0;
	::CloseHandle(hPipe);
	hPipe = 0;
}

bool Worker::start_capture(int left, int top, int width, int height)
{
	int  i;
	char cmd[256];
	std::string res;
	sprintf(cmd,"CRECO %d %d %d %d",left,top,width,height);
	if(request_sync(cmd,res)){
		sscanf(res.c_str(),"SRECO %d",&i);
		return (i==1);
	}
	return false;
}

bool Worker::stop_capture()
{
	int  i;
	char cmd[256];
	std::string res;
	sprintf(cmd,"CSTOP");
	if(request_sync(cmd,res)){
		sscanf(res.c_str(),"SSTOP %d",&i);
		return (i==1);
	}
	return false;
}

bool Worker::is_recording(){
	std::string res;
	int  i;
	if(request_sync("CSTAT",res)){
		sscanf(res.c_str(),"SSTAT %d",&i);
		res += "\n";
		logmsg(res.c_str());
		if(i==1) logmsg("is recording \n");
		return (i==1);
	}else{
		logmsg("get state error\n");
	}
	return false;
}